
#include "../iface/testerror.h"
#include <apr_strings.h>
#include "../aprtools/APRAutoMemoryPool.h"
#include "../net/Broker5Client.h"
#include "RemoteResolver.h"

/** Constructor for the remote resolver class.
 * 
 * @param ikeydb The key database to be used to authenticate this client.
 * 
 * @param ihostname The host name of the broker containing information on the actors.
 * 
 * @param iportno The port number of the broker.
 * 
 * @param isender The actor ID of the sending actor.
 * 
 * @param irecipient The actor ID of the recipient this is to field the actor requests.
 *
 * @param imp The memory pool to use to derive a new set of memory pools for this class. */
RemoteResolver::RemoteResolver(KeyDB &ikeydb, 
   const char *ihostname, int iportno, const char *isender, const char *irecipient,
   apr_pool_t *imp) : Resolver(imp)
{
   // This is a constructor, this must be left blank
   FATAL_IF_BLANK(, ihostname, "host name");
   FATAL_IF_BLANK(, isender, "sender");
   FATAL_IF_BLANK(, irecipient, "recipient");
   keydb = &ikeydb;
   hostname = apr_pstrdup(pmp, ihostname);
   portno = iportno;
   sender = apr_pstrdup(pmp, isender);
   recipient = apr_pstrdup(pmp, irecipient);
   valid = 1;
}

/** Destructor for the remote resolver class. */
RemoteResolver::~RemoteResolver()
{
   
}

/** Exchange a message that resolves both names and types of an actor.
 * 
 * @param hierarchy The human-readable namespaced name.
 * 
 * @return Zero on success, one on error, negative on unsuccessful message exchange. */
int RemoteResolver::exchangeMessageToNameAndTypeResolve(const char *hierarchy)
{
   APRAutoMemoryPool pool(mp);
   BrokerMessage bmsg(pool.mp());
   bmsg.setRecipient(recipient);
   bmsg.setSender(sender);
   bmsg.addTypeHeaderNOCRLF("RESOLVE_NAME", sender);
   bmsg.addHeaderLineNOCRLF("Hierarchical-Namespace: %s", hierarchy);
   Broker5Client::exchangeMessage(bmsg, pool.mp(), NULL, hostname, portno, *keydb, 1000000);
   if (bmsg.isProcessee("SUCCESS"))
   {
      const APRTable *headers = bmsg.getHeaderTable();
      if ((headers->get("Real-ID")) && (strlen(headers->get("Real-ID")) > 0))
      {
         caching_map[std::string(hierarchy)] = std::string(headers->get("Real-ID"));
         caching_map[std::string(hierarchy) + std::string(".id")] = std::string(headers->get("Real-ID"));
         if (strstr(headers->get("Real-ID"), "."))
         {
            caching_map[std::string(hierarchy) + std::string(".name")] = std::string(&((strstr(hierarchy, "."))[1]));
         } else
         {
            caching_map[std::string(hierarchy) + std::string(".name")] = std::string(hierarchy);
         }
         if (headers->get("Real-Type"))
            caching_map[std::string(hierarchy) + std::string(".type")] = std::string(headers->get("Real-Type"));
         return 0;
      } else
      {
         errprint("successful query didn't come with a real actor ID");
         return 1;
      }
   }
   return -1;
}

/** Rename the human-readable part of an actor.
 * 
 * @param actorid The unique internal ID of the actor.
 * 
 * @param actoralias The alias of the actor. */
void RemoteResolver::renameActorID(const char *actorid, const char *actoralias)
{
   if (validateNewNameNotIllegal(actoralias) < 0)
      errprint("illegal actor alias '%s'", actoralias);
   
}

/** Resolve a name (internal ID) from the given human-readable namespace hierarchy.
 * 
 * @param hierarchy The human-readable namespaced name.
 * 
 * @return The internal ID, or NULL if it was not found. */
const char *RemoteResolver::resolveName(const char *hierarchy)
{
   if (!valid)
   {
      errprint("invalid remote resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, hierarchy, "hierarchical query");
   return (exchangeMessageToNameAndTypeResolve(hierarchy) == 0) ? getCached(hierarchy) : NULL;
}

/** Resolve an actor type from the given human-readable namespace hierarchy.
 * 
 * @param hierarchy The human-readable namespaced name.
 * 
 * @return The actor type, or NULL if it was not found. */
const char *RemoteResolver::resolveType(const char *hierarchy)
{
   if (!valid)
   {
      errprint("invalid remote resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, hierarchy, "hierarchical query");
   std::string nstring(std::string(hierarchy) + std::string(".type"));
   return (exchangeMessageToNameAndTypeResolve(hierarchy) == 0) ? getCached(nstring.c_str()) : NULL;
}

/** Resolve a progress value from the given human-readable namespace hierarchy.
 * 
 * @param hierarchy The human-readable namespaced name.
 * 
 * @return The progress value as a string, or NULL if it was not found. */
const char *RemoteResolver::resolveProgress(const char *hierarchy)
{
   if (!valid)
   {
      errprint("invalid remote resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, hierarchy, "hierarchical query");
   APRAutoMemoryPool pool(mp);
   BrokerMessage bmsg(pool.mp());
   bmsg.setRecipient(recipient);
   bmsg.setSender(sender);
   bmsg.addTypeHeaderNOCRLF("RESOLVE_PROGRESS", sender);
   bmsg.addHeaderLineNOCRLF("Hierarchical-Namespace: %s", hierarchy);
   Broker5Client::exchangeMessage(bmsg, pool.mp(), NULL, hostname, portno, *keydb, 1000000);
   if (bmsg.isProcessee("SUCCESS"))
   {
      const APRTable *headers = bmsg.getHeaderTable();
      if ((headers->get("Real-Progress")) && (strlen(headers->get("Real-Progress")) > 0))
      {
         double progress_amt = atof(headers->get("Real-Progress"));
         std::string nstring(std::string(hierarchy) + std::string(".progress"));
         // if complete, we can cache this permanently
         if (progress_amt < 1.0)
            return cacheTempAndReturn(nstring.c_str(), headers->get("Real-Progress"));
         else
            return cacheAndReturn(nstring.c_str(), headers->get("Real-Progress"));
      }
   }
   return NULL;
}

/** Resolve metadata about a given hierarchy-named actor.
 * 
 * @param parent The human-readable namespaced name of the actor whose
 * metadata is to be queried.
 * 
 * @param suffix The suffix denoting which metadata piece is to be
 * returned from this function.
 * 
 * @return The data as a string. It is NULL if it is not found, or an
 * empty string if there is no data. */
const char *RemoteResolver::resolveMetadata(const char *parent, const char *suffix)
{
   if (!valid)
   {
      errprint("invalid remote resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, parent, "parent hierarchical query");
   FATAL_IF_BLANK(NULL, suffix, "suffix for information required");
   APRAutoMemoryPool pool(mp);
   BrokerMessage bmsg(pool.mp());
   bmsg.setRecipient(recipient);
   bmsg.setSender(sender);
   bmsg.addTypeHeaderNOCRLF("RESOLVE_METADATA", sender);
   bmsg.addHeaderLineNOCRLF("Hierarchical-Namespace: %s.%s", parent, suffix);
   bmsg.addHeaderLineNOCRLF("Hierarchical-Parent: %s", parent);
   Broker5Client::exchangeMessage(bmsg, pool.mp(), NULL, hostname, portno, *keydb, 1000000);
   // Got a reply... let's see what it was...
   if (bmsg.isProcessee("SUCCESS"))
   {
      const APRTable *headers = bmsg.getHeaderTable();
      int count = 0;
      while (Resolver::metadata_names[count].simple_key != NULL)
      {
         if (headers->get("%s", metadata_names[count].broker_message_key))
         {
            // this one is in the metadata received
            Resolver::cacheabletype cache_it = metadata_names[count].cacheable;
            int should_cache = 0;
            const char *result = headers->get("%s", metadata_names[count].broker_message_key);
            if ((strlen(result) > 0) && (cache_it == Resolver::ITEM_CACHEABLE_IF_NONEMPTY))
               should_cache = 1;
            if (cache_it == Resolver::ITEM_CACHEABLE_ALWAYS)
               should_cache = 1;
            if (should_cache)
            {
               caching_map[std::string(parent) + std::string(".") + std::string(metadata_names[count].simple_key)] = std::string(result);
            } else
            {
               // caching it anyway, but shouldn't rely on the accuracy of this without updates
               caching_map[std::string(parent) + std::string(".") + std::string(metadata_names[count].simple_key) + std::string("_cached")] = std::string(result);
            }
         }
         // else errprint("didn't get '%s' (%s) in metadata", metadata_names[count].broker_message_key, metadata_names[count].simple_key);
         count++;
      }
      std::string nstring(std::string(parent) + std::string(".") + std::string(suffix));
      const char *returnable = NULL;
      returnable = getCached(nstring.c_str());
      if (returnable) return returnable;
      returnable = getTempCached(nstring.c_str());
      if (returnable) return returnable;
      errprint("could not find requested item '%s.%s'", parent, suffix);
      return NULL;
   }
}

/** Resolve data about the parent of a given hierarchy-named actor.
 * 
 * @param parent The human-readable namespaced name of the actor whose
 * parent is to be queried.
 * 
 * @param suffix The suffix denoting what data is to be gathered about
 * the parent.
 * 
 * @return The data as a string. It is NULL if it is not found, or an
 * empty string if there is not parent. */
const char *RemoteResolver::resolveParent(const char *parent, const char *suffix)
{
   if (!valid)
   {
      errprint("invalid remote resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, parent, "parent hierarchical query");
   FATAL_IF_BLANK(NULL, suffix, "suffix for information required");
   APRAutoMemoryPool pool(mp);
   BrokerMessage bmsg(pool.mp());
   bmsg.setRecipient(recipient);
   bmsg.setSender(sender);
   bmsg.addTypeHeaderNOCRLF("RESOLVE_PARENT", sender);
   bmsg.addHeaderLineNOCRLF("Hierarchical-Namespace: %s.%s", parent, suffix);
   bmsg.addHeaderLineNOCRLF("Hierarchical-Parent: %s", parent);
   Broker5Client::exchangeMessage(bmsg, pool.mp(), NULL, hostname, portno, *keydb, 1000000);
   // Got a reply... let's see what it was...
   if ((bmsg.isProcessee("SUCCESS")) && (bmsg.getHowManyPayloads() == 2))
   {
      const char *parent_ids = bmsg.getPayload(0);
      const char *parent_names = bmsg.getPayload(1);
      std::string nstring(std::string(parent) + std::string(".parent"));
      cacheAndReturn(nstring.c_str(), parent_ids);
      std::string nstring2(std::string(parent) + std::string(".parentname"));
      cacheAndReturn(nstring2.c_str(), parent_names);
      if (strcmp(suffix, "parent") == 0) return getCached(nstring.c_str());
      if (strcmp(suffix, "parentname") == 0) return getCached(nstring2.c_str());
      return NULL;
   } else if (bmsg.isProcessee("SUCCESS"))
   {
      return NULL;
   } else errprint("did not get successful response for parent request");
   return NULL;
}

/** Resolve data about the children of a given hierarchy-named actor.
 * 
 * @param parent The human-readable namespaced name of the actor whose
 * children are to be queried.
 * 
 * @param suffix The suffix denoting what data is to be gathered about
 * the children.
 * 
 * @return The data as a string. It is NULL if it is not found, or an
 * empty string if there are no children. */
const char *RemoteResolver::resolveChildren(const char *parent, const char *suffix)
{
   if (!valid)
   {
      errprint("invalid remote resolver - can't resolve");
      return NULL;
   }
   FATAL_IF_BLANK(NULL, parent, "parent hierarchical query");
   FATAL_IF_BLANK(NULL, suffix, "suffix for information required");
   APRAutoMemoryPool pool(mp);
   BrokerMessage bmsg(pool.mp());
   bmsg.setRecipient(recipient);
   bmsg.setSender(sender);
   bmsg.addTypeHeaderNOCRLF("RESOLVE_CHILDREN", sender);
   bmsg.addHeaderLineNOCRLF("Hierarchical-Namespace: %s.%s", parent, suffix);
   bmsg.addHeaderLineNOCRLF("Hierarchical-Parent: %s", parent);
   Broker5Client::exchangeMessage(bmsg, pool.mp(), NULL, hostname, portno, *keydb, 1000000);
   // Got a reply... let's see what it was...
   if ((bmsg.isProcessee("SUCCESS")) && (bmsg.getHowManyPayloads() == 2))
   {
      const char *children_ids = bmsg.getPayload(0);
      const char *children_names = bmsg.getPayload(1);
      std::string nstring(std::string(parent) + std::string(".children"));
      cacheAndReturn(nstring.c_str(), children_ids);
      std::string nstring2(std::string(parent) + std::string(".childrennames"));
      cacheAndReturn(nstring2.c_str(), children_names);
      if (strcmp(suffix, "children") == 0) return getCached(nstring.c_str());
      if (strcmp(suffix, "childrennames") == 0) return getCached(nstring2.c_str());
      return NULL;
   } else if (bmsg.isProcessee("SUCCESS"))
   {
      return NULL;
   } else errprint("did not get successful response for children request");
   return NULL;
}
